// 遵守 ICMP 协议的时间服务器服务器程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <netinet/ip_icmp.h>

#define BUFFER_SIZE 1024

unsigned short in_cksum(unsigned short *addr, int len);

// 处理ICMP请求
void handle_icmp_request(int sockfd, struct sockaddr_in client_addr) {
    char buffer[BUFFER_SIZE];
    socklen_t client_addr_len = sizeof(client_addr);
    // 接收ICMP请求
    ssize_t bytes_received = recvfrom(sockfd, buffer, sizeof(buffer), 0,
                                      (struct sockaddr *)&client_addr, &client_addr_len);
    if (bytes_received < 0) {
        perror("recvfrom");
        exit(EXIT_FAILURE);
    }

    // 获取当前时间
    struct timeval tv;
    gettimeofday(&tv, NULL);
    struct tm *tm_info = localtime(&tv.tv_sec);
    char time_str[64];
    strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S", tm_info);

    // 构造ICMP响应
    struct icmphdr *icmp_hdr = (struct icmphdr *)buffer;
    icmp_hdr->type = ICMP_ECHOREPLY;
    icmp_hdr->code = 0;
    icmp_hdr->checksum = 0;
    // 计算校验和
    icmp_hdr->checksum = in_cksum((unsigned short *)icmp_hdr, bytes_received);

    // 将时间信息填充到ICMP数据部分
    memcpy(buffer + sizeof(struct icmphdr), time_str, strlen(time_str));

    // 发送ICMP响应
    ssize_t bytes_sent = sendto(sockfd, buffer, bytes_received, 0,
                                (struct sockaddr *)&client_addr, client_addr_len);
    if (bytes_sent < 0) {
        perror("sendto");
        exit(EXIT_FAILURE);
    }
}

// 计算ICMP校验和
unsigned short in_cksum(unsigned short *addr, int len) {
    register int nleft = len;
    register unsigned short *w = addr;
    register int sum = 0;
    unsigned short answer = 0;

    while (nleft > 1) {
        sum += *w++;
        nleft -= 2;
    }
    if (nleft == 1) {
        unsigned short u = 0;
        *(unsigned char *)&u = *(unsigned char *)w;
        sum += u;
    }
    sum = (sum >> 16) + (sum & 0xffff);
    sum += (sum >> 16);
    answer = ~sum;
    return answer;
}

int main() {
    int sockfd;
    struct sockaddr_in server_addr, client_addr;

    // 创建原始套接字
    sockfd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
    if (sockfd < 0) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    // 绑定服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    printf("ICMP Time Server is running...\n");
    while (1) {
        handle_icmp_request(sockfd, client_addr);
    }

    close(sockfd);
    return 0;
}